跳到主要内容

刷新整棵树还是某个节点,获取子节点为什么不能做同步重 IO?

结论​

能确定范围时,onDidChangeTreeData 只发射那个节点,编辑器会重新要它的子节点。只有根的结构变了才刷新整棵树。getChildren 可以异步,但不能在里面同步读大目录、同步起进程或同步等网络,否则扩展宿主被堵住,所有视图一起卡。

展开​

this.emitter.fire(changedNode); // 只刷新这一支
this.emitter.fire(undefined); // 整棵树

局部刷新保留其余节点的展开状态,也少很多次 getChildren。节点如果是新对象,要用稳定 id,编辑器才知道它还是原来那个。

getChildren 返回 ProviderResult,可以是 Promise。异步加载时先返回 Promise,编辑器会显示忙碌。结果回来再渲染孩子。

禁止的是同步阻塞:

  • fs.readFileSync 扫整个仓库
  • execSync 等 git
  • 在循环里同步解大量 JSON

这些调用发生在扩展宿主的主线程上。树一展开,命令、其他插件的补全都得等它返回。

数据量大时:

  • 孩子分页或「加载更多」节点
  • 缓存一层,文件监视或命令成功后再让对应节点失效
  • 搜索用单独的筛选,不要为了过滤把所有叶子一次建出来

根节点很多时,先给一个父节点,不要把十万文件铺在第一层。

容易讲错的点​

  • 异步不等于可以不取消。用户折叠又展开时,旧请求回来不要把过期孩子写进新节点。
  • 每次按键都 fire(undefined),树会闪,展开状态也可能丢。
  • 在 getChildren 里弹 showInformationMessage 询问用户,会让取孩子的过程卡在模态框上。